﻿using System;
using System.IO;
//using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Collections;
using System.Resources;
using System.Reflection;
using System.Security.Cryptography;


namespace Mimoza.Utils
{
    public class MyComparerClass : IComparer
    {
        // Calls CaseInsensitiveComparer.Compare with the parameters reversed.
        int IComparer.Compare(Object x, Object y)
        {
            byte[] x1 = (byte[])x;
            byte[] y1 = (byte[])y;

            for (int i = 0; i < 3; i++)
            {
                if (x1[i] < y1[i]) return -1;
                if (x1[i] > y1[i]) return 1;
            }
            return 0;
        }
    }

    public class MACList
    {
        public MACList() { }

        public byte[] root = new byte[9];
        public ArrayList list = new ArrayList();
    }

    public class ReadMACs
    {
        public ArrayList readedMACs = new ArrayList();

        public ReadMACs()
        { }
        public void Do(string fileName)
        {

            try
            {
                using (StreamReader sr = new StreamReader(fileName))
                {
                    int i, lineNumber = 0;
                    byte[] mac;
                    byte[] root = new byte[12];
                    byte[] current = new byte[12];
                    String line, l;
                    MACList mlist;

                    // Read and display lines from the file until the end of 
                    // the file is reached.
                    while ((l = sr.ReadLine()) != null)
                    {
                        line = l.Trim();
                        i = 0;
                        lineNumber++;

                        root.Initialize();

                        foreach (char c in line)
                        {
                            root[i] = (byte)Convert.ToInt16(c.ToString(), 16);
                            i++;

                            if (i > 12)
                            {
                                Console.WriteLine("MAC address '" + line + "' to long: Error in line " + lineNumber.ToString());
                                break;
                            }
                        }

                        if (i < 12)
                        {
                            Console.WriteLine("MAC address '" + line + "' to short: Error in line " + lineNumber.ToString());
                            continue;
                        }

                        if (i > 12)
                        {
                            continue;
                        }

                        mlist = null;
                        foreach (MACList m in readedMACs)
                        {
                            mlist = m;
                            for (int k = 0; k < 9; k++)
                            {
                                if (mlist.root[k] != root[k])
                                {
                                    mlist = null;
                                    break;
                                }
                            }

                            if (mlist != null)
                            {
                                break;
                            }
                        }

                        if (mlist == null)
                        {
                            mlist = new MACList();
                            Array.Copy(root, 0, mlist.root, 0, 9);

                            readedMACs.Add(mlist);
                        }

                        mac = new byte[3];
                        Array.Copy(root, 9, mac, 0, 3);
                        mlist.list.Add(mac);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("The file could not be read:");
                Console.WriteLine(e.Message);
            }
        }
    }

    public class WriteMACs
    {
        private const int MaxNumberInThread = 1000;
        private ArrayList readedMACs;

        public WriteMACs(ArrayList macs)
        {
            readedMACs = macs;
        }

        public void Do(string modelName, string OSName, string version, string fileName)
        {
            byte[] lastStr;
            int writedNumbers;
            int writedNumbersInThread;
            string datFileName = fileName + ".dat";
            Mimoza.Common.LicenceFile licenceFile = new Mimoza.Common.LicenceFile();

            licenceFile.ModelName = modelName;
            licenceFile.OSName = OSName;
            licenceFile.Version = version;

            if (modelName == Mimoza.Common.Model.C36SX_DisplayName)
            {
                licenceFile.Model = Mimoza.Common.ModelEnum.C36SX;
            }
            else if (modelName == Mimoza.Common.Model.L66_DisplayName)
            {
                licenceFile.Model = Mimoza.Common.ModelEnum.L66;
            }
            else if (modelName == Mimoza.Common.Model.LX_DisplayName)
            {
                licenceFile.Model = Mimoza.Common.ModelEnum.LX;
            }
            else if (modelName == Mimoza.Common.Model.VPC_DisplayName)
            {
                licenceFile.Model = Mimoza.Common.ModelEnum.VPC;
            }
            else 
            {
                throw new Exception("Unknown platform - " + modelName);
            }

            using (FileStream fs = new FileStream(datFileName, FileMode.Create))
            using (BinaryWriter w = new BinaryWriter(fs))
            {
                licenceFile.WriteToStream(fs);
                #region Write MACs foreach (MACList m in readedMACs )
                foreach (MACList m in readedMACs)
                {
                    m.list.Sort(new MyComparerClass());

                    writedNumbers = 0;
                    writedNumbersInThread = 0;

                    #region foreach ( byte[] str in m.list )
                    foreach (byte[] str in m.list)
                    {
                        //write header of the thteard
                        if (writedNumbersInThread == 0 || writedNumbersInThread > MaxNumberInThread - 1)
                        {
                            int count;
                            //Console.WriteLine("Start new thread");

                            foreach (byte c in m.root)
                            {
                                w.Write(c);
                            }

                            if (m.list.Count - writedNumbers > MaxNumberInThread)
                            {
                                count = MaxNumberInThread;
                            }
                            else
                            {
                                count = m.list.Count - writedNumbers;
                            }

                            //Console.Write(" = " + count.ToString() + " last mac = ");
                            w.Write((Int16)count);

                            writedNumbersInThread = 0;

                            lastStr = (byte[])m.list[writedNumbers + count - 1];
                            foreach (byte c in lastStr)
                            {
                                w.Write(c);
                            }
                        }

                        writedNumbers++;
                        writedNumbersInThread++;

                        foreach (byte c in str)
                        {
                            w.Write(c);
                        }
                    }
                    #endregion
                }
                #endregion
                #region Arrange size of file up to 1M
                int randomBytes = 1048576 - (int)(fs.Length % 1048576);
                for (int i = 0; i < randomBytes; i++)
                {
                    char c = (char)(0);
                    w.Write(c);
                }
                #endregion

                SingMACS(fileName, fs);
                bool res = VerifySingMACS(fileName, fs);
                Console.WriteLine("Result - " + res.ToString());
            }
        }

        private Stream GetSerealizedKey(string name)
        {
            string path = Assembly.GetExecutingAssembly().Location;
            path = path.Substring(0,path.LastIndexOf('\\')) + @"\";
            return new FileStream(path + name, FileMode.Open,FileAccess.Read);
            //string name1 = Assembly.GetExecutingAssembly().GetName().CodeBase;
            //Assembly asm = Assembly.LoadFrom(name1);

            //if (asm != null)
            //{
            //    string[] strArr = asm.GetManifestResourceNames();
            //    if (strArr != null && strArr.Length > 0)
            //    {
            //        foreach (string str in strArr)
            //        {
            //            if (str.EndsWith(name))
            //            {
            //                return asm.GetManifestResourceStream(str);
            //                //Stream stream = asm.GetManifestResourceStream(str);
            //                //return new XmlTextReader(stream);
            //            }
            //        }
            //    }
            //}
            //return null;
        }

        private bool VerifySingMACS(string filename, Stream dataStream)
        {
            dataStream.Position = 0;

            byte[] sign;
            byte[] data = new byte[dataStream.Length];
            dataStream.Read(data, 0, (int)dataStream.Length);

            RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();

            using (Stream file = GetSerealizedKey("PublicKey.bin"))
            {
                byte[] blob = new byte[file.Length];
                file.Read(blob, 0, (int)file.Length);
                RSAalg.ImportCspBlob(blob);
            }

            using (FileStream file = new FileStream(filename + ".sign", FileMode.Open, FileAccess.Read))
            {
                sign = new byte[file.Length];
                file.Read(sign, 0, (int)file.Length);

                Array.Reverse(sign);
            }

            return RSAalg.VerifyData(data, new SHA1CryptoServiceProvider(), sign);
        }

        private void SingMACS(string filename, Stream dataStream)
        {
            dataStream.Position = 0;

            RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();

            using (Stream file = GetSerealizedKey("PrivateKey.bin"))
            {
                byte[] blob = new byte[file.Length];
                file.Read(blob, 0, (int)file.Length);
                RSAalg.ImportCspBlob(blob);
            }

            #region Export Public Key
            using (StreamWriter sw = new StreamWriter("PublicKey.txt"))
            {
                byte[] pubKey = RSAalg.ExportCspBlob(false);
                foreach (byte b in pubKey)
                {
                    sw.Write(b.ToString() + ",");
                }
            }
            #endregion

            // Hash and sign the data. Pass a new instance of SHA1CryptoServiceProvider
            // to specify the use of SHA1 for hashing.

            using (FileStream file = new FileStream(filename + ".sign", FileMode.CreateNew))
            {
                SHA1Managed hash = new SHA1Managed();
                byte[] hashedData;
                byte[] data = new byte[dataStream.Length];
                dataStream.Read(data, 0, (int)dataStream.Length);
                hashedData = hash.ComputeHash(data);
                byte[] signedData = RSAalg.SignHash(hashedData, CryptoConfig.MapNameToOID("SHA1"));

                //Reverse signature for using in C++
                Array.Reverse(signedData);
                file.Write(signedData, 0, signedData.Length);
            }
        }
    }

    class CompressMACs
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            try
            {
                ReadMACs r_macs = new ReadMACs();
                WriteMACs w_macs = new WriteMACs(r_macs.readedMACs);

                if (args.Length != 5)
                {
                    Console.WriteLine("The syntax of the command is incorrect:" + args.Length.ToString());
                    Console.WriteLine("   Use the following syntax 'CompressMACs  ModelName OSNAme Version sourceFILE destinationFILE'");
                }
                else
                {
                    r_macs.Do(args[3]);
                    w_macs.Do(args[0], args[1], args[2], args[4]);
                }
            }
            catch (Exception e)
            {
                Console.Write("Exception was occuried:" + e.ToString());
                Mimoza.Common.Logger.Log.Error("CompressMACs: Progarm.Main error - '" + e.ToString() + "'.");
            }
        }
    }
}
